S03-02 核心类-包装类
[TOC]
包装类
在 Java 中,包装类(Wrapper Class) 是一个非常基础且核心的概念。Java 是一种面向对象的语言,但为了性能考量,它保留了 8 种基本数据类型(如 int, double 等),这些基本类型并不是对象。
为了让基本数据类型也能拥有对象的特征(例如参与面向对象的操作、调用方法、放入集合中),Java 为每一种基本数据类型都提供了一个对应的包装类。
基本数据类型 vs 包装类
基本数据类型与包装类的对应关系:
Java 的 java.lang 包中包含了 8 种基本数据类型的包装类。它们的对应关系非常简单,除了 int 和 char 之外,其他的只需将首字母大写即可:
| 基本数据类型 (Primitive Type) | 包装类 (Wrapper Class) | 父类 |
|---|---|---|
byte | Byte | Number |
short | Short | Number |
int | Integer | Number |
long | Long | Number |
float | Float | Number |
double | Double | Number |
char | Character | Object |
boolean | Boolean | Object |
为什么需要包装类
为什么需要包装类:
既然有了基本数据类型,为什么还要多此一举设计包装类呢?主要有以下几个原因:
集合框架(Collections)的限制:Java 中的集合(如
List,Set,Map)只能存储对象,不能直接存储基本数据类型。如果你想创建一个包含整数的列表,只能写List<Integer>,而不能写List<int>。表示“空值”(null):基本数据类型有默认值(如
int默认是 0),但无法表示“没有值”的状态。而在实际开发(尤其是数据库操作和 Web 请求)中,0 和null的意义完全不同。包装类作为对象,可以被赋值为null。提供丰富的实用方法和常量:包装类中封装了大量有用的方法。例如,
Integer.parseInt("123")可以将字符串转为整数,Integer.MAX_VALUE可以直接获取int类型的最大值。
自动装箱与自动拆箱
自动装箱(Autoboxing)与自动拆箱(Unboxing):
在 Java 5 之前,基本类型和包装类之间的转换需要手动进行,代码显得非常繁琐。从 Java 5 开始,引入了自动装箱和自动拆箱机制。
自动装箱:Java 编译器自动将基本数据类型转换为对应的包装类。
- 底层原理:编译器在编译时调用了包装类的
valueOf()方法。
- 底层原理:编译器在编译时调用了包装类的
自动拆箱:Java 编译器自动将包装类转换为对应的基本数据类型。
- 底层原理:编译器在编译时调用了包装类的 Number 超类的
xxxValue()方法(如intValue())。
- 底层原理:编译器在编译时调用了包装类的 Number 超类的
示例:
Java 5 之前的写法(手动)
java// Java 5 之前的写法(手动) Integer numObj = Integer.valueOf(100); // 装箱 int num = numObj.intValue(); // 拆箱Java 5 之后的写法(自动装箱与拆箱)
java// Java 5 之后的写法(自动装箱与拆箱) Integer a = 10; // 自动装箱:等同于 Integer.valueOf(10); int b = a; // 自动拆箱:等同于 a.intValue(); // 甚至可以在数学运算中自动混用 Integer x = 5; Integer y = x + 5; // 先把 x 拆箱变成 5,相加得到 10,再把 10 自动装箱赋值给 y
内存结构变化
转换成包装类后,内存结构有如下变化
public static void main(String[] args) {
int num = 520;
Integer obj = new Integer(520);
}
常量池缓存
包装类的常量池缓存机制(面试高频考点):
为了提高性能并减少内存空间的占用,Java 在 Byte, Short, Integer, Long, Character, Boolean 这几个包装类中实现了缓存机制(Cache)。
以最常用的 Integer 为例,Java 内部维护了一个 IntegerCache,默认缓存了 -128 到 127 之间的 Integer 对象。
当你通过自动装箱(即 Integer.valueOf(i))创建一个在该范围内的对象时,Java 会直接从缓存池中返回已经存在的对象,而不会 new 一个新对象;如果超出这个范围,则会去堆内存中 new 一个新对象。
Integer a = 100;
Integer b = 100;
System.out.println(a == b); // true (都在 -128 ~ 127 范围内,指向缓存池中的同一个对象)
Integer c = 200;
Integer d = 200;
System.out.println(c == d); // false (超出了范围,底层每次都 new 了一个新对象,内存地址不同)注意:
Double和Float并没有实现缓存机制,因为在任何范围内,浮点数都有无限多个。
包装类比较
包装类比较时的注意事项:
因为包装类是对象,所以在进行相等性比较时,必须非常小心:
比较值是否相等,必须使用
.equals()方法,千万不要使用==(除非你在比较基本数据类型)。==比较的是对象的内存地址,而.equals()被重写过,比较的是对象内部包裹的值。javaInteger i1 = new Integer(10); Integer i2 = new Integer(10); System.out.println(i1 == i2); // false (new 出来的对象地址一定不同) System.out.println(i1.equals(i2)); // true (值相等)基本类型与包装类用
==比较时,包装类会自动拆箱:javaint num1 = 200; Integer num2 = 200; System.out.println(num1 == num2); // true (num2 会自动拆箱为 int,然后比较数值)
常见类型转换
常见类型转换操作:
包装类在字符串与基本类型之间的转换中扮演着重要角色:
字符串转基本类型:
使用包装类的
parseXxx(String s)方法(最常用):javaint i = Integer.parseInt("123"); double d = Double.parseDouble("3.14");字符串转包装类对象:
使用
valueOf(String s)方法:javaInteger num = Integer.valueOf("123");基本类型转字符串:
除了直接用
"" + 123这种方式外,还可以用String.valueOf():javaString s1 = String.valueOf(123); // "123"

自定义包装类

练习题
练习题:
利用Vector代替数组处理:从键盘读入学生成绩(以负数代表输入结束),找出最高分,并输出学生成绩等级。
提示:数组一旦创建,长度就固定不变,所以在创建数组前就需要知道它的长度。而向量类java.util.Vector可以根据需要动态伸缩。
1、创建Vector对象:Vector v=new Vector();
2、给向量添加元素:v.addElement(Object obj); //obj必须是对象
3、取出向量中的元素:Object obj=v.elementAt(0);
注意第一个元素的下标是0,返回值是Object类型的。
4、计算向量的长度:v.size();
5、若与最高分相差10分内:A等;20分内:B等;30分内:C等;其它:D等
Integer
在 Java 日常开发中,Integer 是使用频率极高的包装类。除了作为 int 的对象形态存在,它内部还封装了大量极其好用的静态方法和常量,涵盖了类型转换、进制转换、数值比较和位运算等场景。
常量
核心常量:
Integer 类提供了一些常量,可以让你直接获取 int 类型的物理属性, 避免硬编码:
| 常量名称 | 描述 | 实际值 |
|---|---|---|
Integer.MAX_VALUE | int 类型能表示的最大值 | 2147483647 () |
Integer.MIN_VALUE | int 类型能表示的最小值 | -2147483648 () |
Integer.SIZE | int 值在内存中占用的位数(Bit) | 32 |
Integer.BYTES | int 值在内存中占用的字节数(Byte) | 4 (Java 8 引入) |
(提示:在求数组最大值/最小值算法的初始化时,经常会用到 MIN_VALUE 和 MAX_VALUE。)
方法
字符串转数字
这是日常开发中最频繁的操作,主要处理前端传来的字符串数据或进行日志打印。
intInteger.parseInt():(String s),将字符串解析为基本数据类型int。如果字符串不是合法的数字(例如包含字母),会抛出NumberFormatException。javaint num = Integer.parseInt("1024");intInteger.parseInt():(String s, int radix),将指定进制的字符串解析为十进制的int。javaint num = Integer.parseInt("FF", 16); // 将 16 进制的 FF 转为 10 进制,结果是 255IntegerInteger.valueOf():(String s),将字符串解析为包装类Integer对象。底层其实就是调用了parseInt,然后再进行装箱。
数字转字符串
StringInteger.toString():(int i),将基本类型int转换为字符串。javaString s = Integer.toString(100); // 相当于 String.valueOf(100)
进制转换
如果你需要将一个十进制整数打印成二进制、八进制或十六进制字符串,Integer 提供了直接的静态方法,无需自己写取余算法:
StringInteger.toBinaryString():(int i),转为二进制字符串。StringInteger.toOctalString():(int i),转为八进制字符串。StringInteger.toHexString():(int i),转为十六进制字符串。javaint num = 28; System.out.println(Integer.toBinaryString(num)); // "11100" System.out.println(Integer.toHexString(num)); // "1c"
对象的创建与缓存
IntegerInteger.valueOf():(int i),这是目前官方唯一推荐的创建Integer对象的方式。避坑指南:自 Java 9 起,构造方法
new Integer(10)已被标记为废弃(Deprecated)。因为valueOf方法内部使用了前面提到的-128 到 127的缓存池机制,性能更好,内存占用更低。
大小比较
intcompareTo():(Integer anotherInteger),实例方法。比较两个Integer对象的值。返回0(相等)、< 0(小于)、> 0(大于)。intInteger.compare():(int x, int y),静态方法。直接比较两个基本类型int的大小。返回值同上。javaSystem.out.println(Integer.compare(10, 20)); // 返回 -1
数学运算@J8
为了配合 Lambda 表达式和 Stream API(如 reduce 操作),Java 8 在 Integer 中新增了几个静态算术方法:
intInteger.max():(int a, int b),返回两者中较大的值。intInteger.min():(int a, int b),返回两者中较小的值。intInteger.sum():(int a, int b),返回两数之和。
底层位运算
在一些追求极致性能的算法、源码或密码学领域,经常会用到 Integer 提供的位级操作方法:
intInteger.bitCount():(int i),返回指定int值的二进制补码表示中,1的个数。(LeetCode 常考题)。java// 7 的二进制是 0000 0111,里面有 3 个 1 System.out.println(Integer.bitCount(7)); // 输出: 3intInteger.reverse():(int i),将二进制位的顺序完全反转。intInteger.highestOneBit():(int i),保留最高位的1,其余全置为0(常用于 HashMap 容量计算的底层逻辑中)。
总结避坑
当你在业务代码中比较两个 Integer 对象的值是否相等时,永远使用 .equals() 方法,绝不要使用 ==。这是因为缓存池(-128 ~ 127)的存在会让 == 在小数值时表现正常,一旦超过这个范围,== 就会因为内存地址不同而返回 false,引发难以排查的 Bug。
Character
在 Java 中,Character 是基本数据类型 char 的包装类。由于 char 在 Java 中占用 2 个字节(16 位),本质上是一个无符号的整数(对应 Unicode 字符集),因此 Character 类不仅提供了对象化的包装,还封装了大量用于字符分类、判断和转换的实用静态方法。
方法
字符类型判断
在处理字符串解析、表单验证或算法题(如验证回文串)时,我们经常需要判断一个字符是字母、数字还是空格。Character 提供了一系列 isXxx() 方法:
| 方法声明(static int) | 功能描述 | 示例与返回值 |
|---|---|---|
isDigit(char ch) | 判断是否为数字 (0-9) | isDigit('5') true |
isLetter(char ch) | 判断是否为字母 (包含中文字符等 Unicode 字母) | isLetter('A') trueisLetter('中') true |
isLetterOrDigit(char ch) | 判断是否为字母或数字 | isLetterOrDigit('!') false |
isWhitespace(char ch) | 判断是否为空白字符(空格、Tab \t、换行 \n 等) | isWhitespace(' ') true |
isUpperCase(char ch) | 判断是否为大写字母 | isUpperCase('a') false |
isLowerCase(char ch) | 判断是否为小写字母 | isLowerCase('A') false |
注意:
isLetter不仅仅识别 26 个英文字母,它基于 Unicode 标准,因此判断汉字等其他语言的字母字符时也会返回true。
大小写转换
如果需要统一字符的格式,可以使用以下两个转换方法。如果传入的字符本身不是字母,或者已经是要转换的大小写,方法会直接返回原字符:
chartoUpperCase():(char ch),将字符转换为大写。chartoLowerCase():(char ch),将字符转换为小写。
char c1 = Character.toUpperCase('a'); // 返回 'A'
char c2 = Character.toLowerCase('B'); // 返回 'b'
char c3 = Character.toUpperCase('5'); // 返回 '5' (非字母不改变)字符与数字的转换
这是一个非常常见的易错点:如果你有一个字符 '9',直接强制转换为 int,得到的是它的 ASCII 码值 57,而不是数字 9。
intgetNumericValue():(char ch),获取字符对应的真实数值。返回指定字符表示的int值。javachar ch = '9'; System.out.println((int) ch); // 输出 57 (这是 ASCII 码) System.out.println(Character.getNumericValue(ch)); // 输出 9 (这是真实的数值)进阶小技巧(减去
'0'):在算法中,更高效且常见的做法是直接利用字符的 ASCII 码差值来获取数字:
javachar ch = '7'; int num = ch - '0'; // 底层是 55 - 48,结果就是 7。比调用方法更快!
缓存机制与对象创建
Character 的缓存机制与对象创建:
与 Integer 类似,Character 也有为了节省内存而设计的常量池缓存机制。
缓存范围:
Character缓存了从\u0000到\u007F(即 0 到 127)的字符,这刚好覆盖了所有标准的 ASCII 字符。推荐创建方式:
Character.valueOf(char c)自 Java 9 起,
new Character('A')同样被废弃,官方推荐使用valueOf()。
Character c1 = Character.valueOf('A'); // ASCII 为 65
Character c2 = Character.valueOf('A');
System.out.println(c1 == c2); // true (命中 0-127 的缓存池,地址相同)
Character c3 = Character.valueOf('中'); // Unicode 远超 127
Character c4 = Character.valueOf('中');
System.out.println(c3 == c4); // false (超出缓存范围,创建了新对象)
System.out.println(c3.equals(c4)); // true (值相等)处理复杂字符
处理复杂字符(Emoji 与补充字符):
这是 Character 类较深的应用场景。标准的 char 是 16 位的,只能表示 Unicode 的基本多语言面(BMP)。但是,像 Emoji 表情(如 😭)或者某些生僻字的 Unicode 编码超过了 16 位,一个 char 是存不下的。
因此,Java 使用两个 char(称为代理对,Surrogate Pair) 来表示这些复杂的字符。
为了处理这种情况,Character 提供了支持传入 int codePoint(代码点,即真实的 Unicode 编码数值)的重载方法:
- static boolean isSupplementaryCodePoint(int codePoint):判断是否为增补字符(如 Emoji)。
- static int charCount(int codePoint):判断该字符需要几个
char来表示(通常是 1 或 2)。